package leducHoldem.Data;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
import java.util.StringTokenizer;

public class Dealer {

	private ArrayList<Move> cards;
	private LinkedList<Move> pack;
	private LinkedList<Move> cardsLeft;
	private LinkedList<Move> cardTypes;
	private byte[] cardsLeftCount;

	public Dealer() {

		pack = new LinkedList<Move>();
		cards = new ArrayList<Move>();
		cardsLeft = new LinkedList<Move>();
		cardTypes = new LinkedList<Move>();
		cardsLeftCount = new byte[] { 2, 2, 2 };

		for (int i = 1; i < 4; i++) {
			Move move = new Move((byte) i);
			cards.add(move);
			cards.add(move);
			cardTypes.add(move);
			cardsLeft.add(move);
		}

		shuffle();
	}
	
	@SuppressWarnings("unchecked")
	public Dealer(Dealer dealer) {
		this.cards = (ArrayList<Move>) dealer.getCards().clone();
		this.pack = (LinkedList<Move>) dealer.getPack().clone();
		this.cardsLeft = (LinkedList<Move>) dealer.getCardsLeft().clone();
		this.cardTypes = (LinkedList<Move>) dealer.getCardTypes().clone();
		this.cardsLeftCount = dealer.getCardsLeftCount().clone();
	}

	public void shuffle() {

		Random random = new Random();
		ArrayList<Move> tempCards = new ArrayList<Move>();

		pack.clear();
		tempCards.addAll(cards);

		for (int i = 0; i < cards.size(); i++) {
			pack.add(tempCards.remove(random.nextInt(tempCards.size())));
		}

	}

	public Move dealCard() {

		Move card = pack.removeFirst();

		cardsLeftCount[card.value - 1]--;

		if (cardsLeftCount[card.value - 1] == 0) {
			cardsLeft.remove(card);
		}

		return card;
	}

	public boolean removeCard(Move card) {
		
		if(card == null) {
			return false;
		}

		if (cardsLeftCount[card.value - 1] == 0) {
			return false;
		}

		cardsLeftCount[card.value - 1]--;
		cards.remove(card);

		if (cardsLeftCount[card.value - 1] == 0) {
			cardsLeft.remove(card);
		}

		return true;
	}

	public void returnCard(Move card) {
		cardsLeftCount[card.value - 1]++;
		cards.add(card);

		if (cardsLeftCount[card.value - 1] == 1) {
			cardsLeft.add(card);
		}
	}
	
	public boolean isChanceSet(String id) {
		
		if(!id.substring(1).equals(id.substring(1).toLowerCase())) {
			return false;
		}
		
		return id.endsWith("c") || id.endsWith("ch.ch");
	}

	public LinkedList<Move> getPossibleMoves(Move lastMove) {

		LinkedList<Move> moves = new LinkedList<Move>();

		if (lastMove == null || lastMove.action.equals("ch")) {
			moves.add(new Move("b"));
			moves.add(new Move("ch"));
		} else if (lastMove.action.equals("b")) {
			moves.add(new Move("c"));
			moves.add(new Move("r"));
			moves.add(new Move("f"));
		} else if (lastMove.action.equals("r")) {
			moves.add(new Move("c"));
			moves.add(new Move("f"));
		} else if (lastMove.action.equals("c")) {
			moves.add(new Move("ch"));
			moves.add(new Move("b"));
		} else if (!lastMove.action.equals("f") && !lastMove.action.equals("end")) {
			moves.add(new Move("b"));
			moves.add(new Move("ch"));
		}

		return moves;
	}

	public ArrayList<Move> getCards() {
		return cards;
	}

	public LinkedList<Move> getCardsLeft() {
		return cardsLeft;
	}

	public LinkedList<Move> getPack() {
		return pack;
	}

	public LinkedList<Move> getCardTypes() {
		return cardTypes;
	}

	public byte[] getCardsLeftCount() {
		return cardsLeftCount.clone();
	}

	public byte evaluate(Move firstCard, Move secondCard, Move table) {

		if (firstCard.equals(secondCard)) {
			return -1;
		}

		if (firstCard.equals(table)) {
			return 1;
		}

		if (secondCard.equals(table)) {
			return 2;
		}

		if (firstCard.value > secondCard.value) {
			return 1;
		}

		return 2;

	}

	public boolean isTerminal(String id) {

		StringTokenizer tokenizer = new StringTokenizer(id, ".");
		String move;
		String lastMove = null;
		byte cardCount = 0;

		while (tokenizer.hasMoreElements()) {

			move = tokenizer.nextToken();

			if (isCard(move)) {
				cardCount++;
			}

			if (isFold(move)) {
				return true;
			}

			if (isCallAfterFlop(move, cardCount)) {
				return true;
			}

			if (isCheckCheckAfterFlop(move, lastMove, cardCount)) {
				return true;
			}

			lastMove = move;
		}

		return false;
	}

	public float[] getCardProbabilities() {

		int sum = 0;
		float[] probabilities = new float[cardsLeftCount.length];

		for (int i = 0; i < cardsLeftCount.length; i++) {
			sum += cardsLeftCount[i];
		}

		for (int i = 0; i < cardsLeftCount.length; i++) {
			probabilities[i] = (float) cardsLeftCount[i] / sum;
		}

		return probabilities;
	}

	public float[] getCardProbabilitiesForCardsOut(LinkedList<Move> cardsOut) {

		float[] probabilities = new float[] { 2, 2, 2 };
		int sum = 0;

		for (Move card : cardsOut) {
			probabilities[card.value - 1]--;
		}

		for (int i = 0; i < probabilities.length; i++) {
			sum += probabilities[i];
		}

		for (int i = 0; i < probabilities.length; i++) {
			probabilities[i] /= sum;
		}

		return probabilities;
	}

	public float[] getCardProbabilitiesForCardsOut(Move playersCard, Move table) {

		float[] probabilities = new float[] { 2, 2, 2 };
		int sum = 0;

		probabilities[playersCard.value - 1]--;
		if (table != null)
			probabilities[table.value - 1]--;

		for (int i = 0; i < probabilities.length; i++) {
			sum += probabilities[i];
		}

		for (int i = 0; i < probabilities.length; i++) {
			probabilities[i] /= sum;
		}

		return probabilities;
	}

	private boolean isCheckCheckAfterFlop(String move, String lastMove, byte cardCount) {
		return "ch".equals(move) && "ch".equals(lastMove) && cardCount == 2;
	}

	private boolean isCallAfterFlop(String move, byte cardCount) {
		return cardCount == 2 && "c".equals(move);
	}

	private boolean isFold(String move) {
		return "f".equals(move);
	}

	private boolean isCard(String move) {
		return !move.toLowerCase().equals(move);
	}

	public boolean hasPlayerOneWon(Move firstCard, Move secondCard, Move table) {
		return evaluate(firstCard, secondCard, table) == 1;
	}

	public boolean hasPlayerTwoWon(Move firstCard, Move secondCard, Move table) {
		return evaluate(firstCard, secondCard, table) == 2;
	}

}
